<!DOCTYPE html>

<html>

<head>
  <title>X Cube Version 2</title>
  <style>
      body {
          /* set margin to 0 and overflow to hidden, to go fullscreen */
          margin: 0;
          overflow: hidden;
      }
  </style>
  <script src="./ex_lib/dat.gui.min.js"></script>
  <script type="importmap">
    {
        "imports": {
          "three": "./ex_lib/three/three.module.js"
        }
      }
  </script>
</head>
<body>

<div id="Stats-output">
</div>
<!-- Div which will hold the Output -->
<div id="WebGL-output">
</div>

<script type="module">
  import * as THREE from 'three'
  import {OrbitControls} from './ex_lib/three/jsm/controls/OrbitControls.js'
  import {Xcube, DragControl, SceneUtils} from "./bundle_es-obfuscated.js";

  // once everything is loaded, we run our Three.js stuff.
  function init() {
    document.addEventListener('mousedown', onDocumentMouseDown, false);
    document.addEventListener('mousemove', onDocumentMouseMove, false);
    document.addEventListener('mouseup', onDocumentMouseUp, false);

    // create a scene, that will hold all our elements such as objects, cameras and lights.
    let scene = new THREE.Scene();
    // show axes in the screen
    let axes = new THREE.AxesHelper(20);
    scene.add(axes);

    let lights = [];
    lights[ 0 ] = new THREE.PointLight( 0xffffff, 1, 0 );
    lights[ 1 ] = new THREE.PointLight( 0xffffff, 2, 3 );
    lights[ 2 ] = new THREE.PointLight( 0xffffff, 1, 0 );

    lights[ 0 ].position.set( 0, 3, 0 );
    lights[ 1 ].position.set( 2, 2, 2 );
    lights[ 2 ].position.set( - 3, - 6, - 3 );
    scene.add( lights[ 0 ] );
    scene.add( lights[ 1 ] );
    scene.add( lights[ 2 ] );

    let spotLight = new THREE.SpotLight(0xffffff);
    spotLight.position.set(-0, 5, 5);
    spotLight.castShadow = true;
    spotLight.intensity = 0.6;
    scene.add(spotLight);

    // create a render and set the size
    let webGLRenderer = new THREE.WebGLRenderer({ antialias: true });
    webGLRenderer.setClearColor( 0x000000, 1 );
    webGLRenderer.setSize(window.innerWidth, window.innerHeight);

    let autoOpLst = [], revertedOpLst, autoOpLstBackUp, autoPlayIndex = 0;
    let pivot = new THREE.Object3D();
    scene.add(pivot);

    let selectedPart = null;
    let selectedArrow;
    let builder = Xcube();
    let parts = builder.createParts();
    let raycastingNeeded = builder.getRaycastingNeeded();
    // let bounding = builder.getBounding();
    // scene.add(bounding);

    parts.forEach(part => scene.add(part));

    let camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 50 );
    camera.position.z = 6;
    camera.position.x = 6;
    camera.position.y = 6;
    let trackballControls = new OrbitControls(camera, webGLRenderer.domElement);

    trackballControls.rotateSpeed = 1.0;
    trackballControls.zoomSpeed = 1.0;
    trackballControls.panSpeed = 1.0;
    trackballControls.staticMoving = true;

    // add the output of the renderer to the html element
    document.getElementById("WebGL-output").appendChild(webGLRenderer.domElement);

    this.getScene = function () {
      return scene;
    };
    this.isAutoRotating = function () {
      return layerRotating;
    };
    this.getCameraControl = function () {
      return trackballControls;
    };
    this.getPivot = function () {
      return pivot;
    };
    this.getIntersects = function (x, y, singleUnit) {
      let vector = new THREE.Vector3(( x / window.innerWidth ) * 2 - 1, -( y / window.innerHeight ) * 2 + 1, 0.5);
      vector = vector.unproject(camera);

      let raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
      if (singleUnit == null) {
        return raycaster.intersectObjects(raycastingNeeded);
      }
      return raycaster.intersectObjects([singleUnit]);
    };
    this.builder = builder;
    let dragControl = DragControl(this);

    let layerRotating = false, rotatingIndex = 0, rotateStep, rotateParts, rotatingTime, rotateAxis, quaternion;
    function applyRotation(operation) {
      rotateAxis = builder.getRotateAxisBy(operation);
      let rotation = builder.getRotation(operation);
      rotatingTime = Math.round(Math.abs(rotation/Math.PI) * controls.rotatePiDuration);

      rotateParts = builder.getParts(operation);
      if (rotateParts.length == 0) {
        console.log("error, invalid operation");
        return false;
      }
      layerRotating = true;
      rotateStep = rotation / rotatingTime;
      rotatingIndex = 0;
      quaternion = new THREE.Quaternion();
      quaternion.setFromAxisAngle( rotateAxis.normalize(), rotateStep);
      pivot.rotation.set( 0, 0, 0 );
      pivot.updateMatrixWorld();
      rotateParts.forEach(part => {
        SceneUtils.attach( part, scene, pivot )
      } );
      return true;
    }

    function onDocumentMouseDown(event) {
      dragControl.mouseDown(event);
    }
    function onDocumentMouseMove(event) {
      dragControl.mouseMove(event);
    }
    function onDocumentMouseUp(event) {
      dragControl.mouseUp(event);
    }

    function copyToClipboard (text) {
      if(text.indexOf('-') !== -1) {
        let arr = text.split('-');
        text = arr[0] + arr[1];
      }
      let textArea = document.createElement("textarea");
      textArea.style.position = 'fixed';
      textArea.style.top = '0';
      textArea.style.left = '0';
      textArea.style.width = '2em';
      textArea.style.height = '2em';
      textArea.style.padding = '0';
      textArea.style.border = 'none';
      textArea.style.outline = 'none';
      textArea.style.boxShadow = 'none';
      textArea.style.background = 'transparent';
      textArea.value = text;
      document.body.appendChild(textArea);
      textArea.select();

      try {
        let successful = document.execCommand('copy');
        let msg = successful ? '成功复制到剪贴板' : '该浏览器不支持点击复制到剪贴板';
        // alert(msg);
      } catch (err) {
        alert('该浏览器不支持点击复制到剪贴板');
      }

      document.body.removeChild(textArea);
    }

    let controls = new function () {
      let reverted = false;
      this.randomRotate = function () {
        if (autoOpLst && autoPlayIndex < autoOpLst.length) {
          return;
        }
        reverted = false;
        autoOpLst = builder.getRandomOperations(controls.randomSteps);
        autoPlayIndex = 0;
        autoOpLstBackUp = autoOpLst.slice();
        revertedOpLst = builder.getRevertedOperations(autoOpLst);
      };
      this.switchRandomRotate = function () {
        console.log("-----------switch-----------")
        if (autoPlayIndex < autoOpLst.length) {
          console.log("cannot revert random rotations, random rotating is in progress.")
          return;
        }
        autoPlayIndex = 0;
        if (!reverted) {
          reverted = true;
          autoOpLst = revertedOpLst.slice();
        } else {
          reverted = false;
          autoOpLst = autoOpLstBackUp.slice();
        }
      };
      this.stepBackwards = function () {
        if (!controls.isPaused || layerRotating) {
          console.log("cannot step backward, until rotate stopped");
          return;
        }
        if (autoPlayIndex < 1) {
          console.log("cannot step backward, already reach beginning");
          return;
        }
        autoPlayIndex--;
        let op = autoOpLst[autoPlayIndex];
        let reverted = builder.getRevertOperation(op);
        applyRotation(reverted);
      };
      this.stepForwards = function () {
        if (!controls.isPaused || layerRotating) {
          console.log("cannot step forward, until rotate stopped");
          return;
        }
        if (autoPlayIndex > autoOpLst.length - 1) {
          console.log("cannot step forward, already reach ending");
          return;
        }
        let op = autoOpLst[autoPlayIndex];
        applyRotation(op);
        autoPlayIndex++;
      };
      this.serializeStatus = function () {
        controls.statusCode = builder.serializeStatus();
        console.log(controls.statusCode);
      };
      this.copyStatusToClipBoard = function () {
        copyToClipboard(controls.statusCode);
      };
      this.deSerialize = function () {
        if (controls.statusCode && controls.statusCode.length > 30) {
          parts.forEach(part => scene.remove(part));
          parts = builder.deSerializeStatus(controls.statusCode);
          parts.forEach(part => scene.add(part));
        }
      };
      this.statusCode = "";
      this.isPaused = false;
      this.rotatePiDuration = 60;
      this.randomSteps = 36;
      this.cornerSize = builder.getSize();
      this.gap = builder.getGap();
    };

    let gui = new dat.GUI();
    gui.add(controls, 'randomRotate').name("random rotate");
    gui.add(controls, 'switchRandomRotate').name("switch random rotate");
    gui.add(controls, 'isPaused').name("pause auto rotate");
    gui.add(controls, 'stepBackwards').name("step backward");
    gui.add(controls, 'stepForwards').name("step forward");
    gui.add(controls, 'randomSteps', 9, 99).name("random steps:").step(1).onChange(function (e) {
      controls.randomSteps = e;
    });
    gui.add(controls, 'rotatePiDuration', 15, 150).name("PI duration").step(1).onChange(function (e) {
      controls.rotatePiDuration = e;
    });
    let saveStatusGui = gui.addFolder("Serialize and Deserialize");
    saveStatusGui.add(controls, 'serializeStatus').name("serialize status");
    saveStatusGui.add(controls, 'statusCode').name("serialized:").listen();
    saveStatusGui.add(controls, 'copyStatusToClipBoard').name("copy serialized string");
    saveStatusGui.add(controls, 'deSerialize').name("deSerialize from text");

    let styleGui = gui.addFolder("X-cube Style");
    styleGui.add(controls, 'cornerSize', 0.1, 1).onChange(function (e) {
      controls.cornerSize = e;
      builder.adjustGapAndPartSize({size: e});
    });
    styleGui.add(controls, 'gap', 0, 1).onChange(function (e) {
      controls.gap = e;
      builder.adjustGapAndPartSize({gap: e});
    });

    render();

    function render() {
      if (!layerRotating && autoOpLst && autoPlayIndex < autoOpLst.length && !controls.isPaused) {
        let autoOp = autoOpLst[autoPlayIndex];
        if(applyRotation(autoOp)) {
          autoPlayIndex++;
        } else {
          autoPlayIndex = autoOpLst.length;
        }
      }
      if (layerRotating) {
        rotatingIndex++;
        pivot.applyQuaternion(quaternion);
        if (rotatingIndex >= rotatingTime) {
          layerRotating = false;
          pivot.updateMatrixWorld();
          rotateParts.forEach(part => {
            SceneUtils.detach( part, pivot, scene );
          } );
        }
      }

      trackballControls.update();
      requestAnimationFrame(render);
      dragControl.beforeFrameDraw();
      webGLRenderer.render(scene, camera);
    }

    window.addEventListener( 'resize', function () {

      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();

      webGLRenderer.setSize( window.innerWidth, window.innerHeight );

    }, false );
  }
  window.onload = init;
</script>
</body>
</html>